package org.equivalence.server.sockets;


import java.awt.AWTEventMulticaster;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;

import org.equivalence.common.FileRepository;
import org.equivalence.server.common.IServer;

/**
 * this is a multithreaded server which can be run in its own thread
 * or as a blocking process
 * @author gcc
 *
 */
public class SocketsUpdateServer implements IServer {
	
	private int port;
	private FileRepository fileRepos;
	private ServerSocket serverSocket;
	ArrayList<String> directories;
	HashMap<String,Boolean> excludeFiles;
	
	private ActionListener eventListener;
	private ActionListener statusListener;
	
	/**
	 * create the server and load its configuration from the confi/server.xml file (if present)
	 *
	 */
	public SocketsUpdateServer(int port) {		
		this.port = port;
		directories = new ArrayList<String>();		
		excludeFiles = new HashMap<String,Boolean> ();
	}
	
	public void setServerPort(int port) {
		this.port = port;		
	}
	
	public int getServerPort() {
		return port;
	}
	
	public void addDirectory(String directory) {
		directories.add(directory);
	}
	
	public void clearDirectories() {
		directories.clear();
	}
	
	public String getDirectory(int i) {
		return directories.get(i);
	}
	
	public void removeDirectory(int i) {
		directories.remove(i);
	}
	
	public int getDirectoryCount() {
		return directories.size();
	}
	
	public void addExcludeFile(String filename) {
		excludeFiles.put(filename, true);
	}
	
	public void removeExcludeFile(String filename) {
		excludeFiles.remove(filename);
	}
	
	public void  addServerEventListener(ActionListener al) {
		eventListener = AWTEventMulticaster.add(
				eventListener, al);
	}
	
	public void  addStatusListener(ActionListener al) {
		statusListener = AWTEventMulticaster.add(
				statusListener, al);
	}
	
	private void changeStatus(String status) {
		if (statusListener != null) {
			statusListener.actionPerformed(new ActionEvent(
					status, ActionEvent.ACTION_PERFORMED,
					"status changed"));
		}
	}
	
	/**
	 * inform any listeners that an event has occured
	 * @param item
	 */
	synchronized void serverEvent(String item) {
		if (eventListener != null) {
			eventListener.actionPerformed(new ActionEvent(
					item, ActionEvent.ACTION_PERFORMED,
					"item logged"));
		}
	}
	
	/**
	 * restart the server
	 *
	 */
	public void init() {
		changeStatus("INITIALISING");
		serverEvent("Initialising server...\r\n");
		fileRepos = new FileRepository(directories,excludeFiles);		
		serverEvent("providing synchronization services for the following directories\r\n");
		for (String s:directories) {
			File newFile = new File(s);
			serverEvent(" "+newFile.getAbsolutePath()+"\r\n");
		}
	}
	
	/**
	 * stop the server
	 *
	 */
	public void stop() {
		//closing the socket while it is awaiting connections will cause an IOException
		//but we have to close it to stop the server
		try {
			serverSocket.close();
		} catch (Exception e) {}
	}
	
	/**
	 * start the server as a blocking process
	 *
	 */
	public void start() {
		changeStatus("STARTING");
		try {
			serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			serverEvent("Server startup Error: "+e.getMessage()+"\r\n");
			changeStatus("STOPPED");
			e.printStackTrace();
			return;
		}
		
		changeStatus("STARTED");
		
		try {			
			serverEvent("Server listening on port "+serverSocket.getLocalPort()+"\r\n");
			serverEvent("awaiting client connections...\r\n");
			
			while (true) {
				//wait for a connection
				Socket socket = serverSocket.accept();
				serverEvent("\r\nNew client connection accepted " +
						   socket.getInetAddress() +
						   ":" + socket.getPort()+"\r\n");
				
				//then spawn a thread
				UpdateRequest request = new UpdateRequest(this,socket,fileRepos);			
				Thread thread = new Thread(request);
				thread.start();
			}
		} catch (IOException e) {
			changeStatus("STOPPED");
			serverEvent("Server Stopped\r\n\r\n");			
		}
	}
	
	/**
	 * start the server in a thread
	 */
	public void run() {	
		start();
	}

}
